package com.ccnu.demojava.net.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

/**
 * @author raohj
 * @date 2018年10月30日
 * @version 0.0.1
 * @comment Netty官方标准例子
 * 2019年3月25日	增加粘包处理
 */
public class RNettyDelimiterServer {
	Logger log = LoggerFactory.getLogger(RNettyDelimiterServer.class);
	
	private String host;
	private int port;
	private int timeout;		// 超时时间
	private int workThreads;	// 工作线程数
	
	public RNettyDelimiterServer(String host, int port, int timeout, int workThreads) {
		this.setHost(host);
		this.setPort(port);
		if(timeout ==0) {
			this.setTimeout(0);
		}
		else {
			this.setTimeout(timeout);
		}
		
		if(workThreads==0) {
			this.setWorkThreads(10);
		}
		else {
			this.setWorkThreads(workThreads);
		}
	}
	
	public String getHost() {
		return host;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}
	
	public int getTimeout() {
		return timeout;
	}

	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	public int getWorkThreads() {
		return workThreads;
	}

	public void setWorkThreads(int workThreads) {
		this.workThreads = workThreads;
	}

	private class RDiscardServerHandler extends ChannelInboundHandlerAdapter {
		// 当有一个连接请求建立时
		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			InetSocketAddress cltSocketAddr = (InetSocketAddress) ctx.channel().remoteAddress();
			log.info("client IP:[" + cltSocketAddr.getAddress() + "] port:[" + cltSocketAddr.getPort() + "] 已连接");
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			ByteBuf in = (ByteBuf) msg;
			try {
				log.info("recv:" + in.toString(Charset.forName("GBK")));
//				String rspMsg = new String("this is my Server, Please deal later");
				String rspMsg = new String("<?xml version=\"1.0\" encoding=\"GBK\"?><ROOT><SvcBody><responseMessage>success</responseMessage><businessQueryPersonDetail><riskGrade>1</riskGrade><taxResident>1</taxResident><qualify>20</qualify><highestDegree>9</highestDegree><gender>1</gender><gradeUpgrade>D</gradeUpgrade><birthDate>1985-04-15</birthDate><residentFlag>1</residentFlag><industry>2</industry><familyMonthRevenue>20000.00</familyMonthRevenue><infoString>130204198504150013</infoString><openAcctOrg>802011</openAcctOrg><whetherIndividualMerchant>0</whetherIndividualMerchant><administrativeDivisionsCode>370500</administrativeDivisionsCode><nation>01</nation><opendt>20181202</opendt><flag>0</flag><lastModifiedByUserLogin>M0001</lastModifiedByUserLogin><partyId>1000413031</partyId><whetherFarmer>1</whetherFarmer><contactCertificateTypeId>101</contactCertificateTypeId><whetherBankStaff>0</whetherBankStaff><liveStatus>1</liveStatus><telNumType>1</telNumType><maritalStatus>20</maritalStatus><depositorCategory>01</depositorCategory><openAcctDate>2016-09-28</openAcctDate><statusId>A</statusId><contactInfo><contactNum>18914785236</contactNum><contactinfoID>1700019</contactinfoID><contacttype>9</contacttype></contactInfo><certificateInfo><thruDate>20501020</thruDate><authAddrCode>1240</authAddrCode><contactCertificateId>1555012</contactCertificateId><certificateValid>20501020</certificateValid><isMainCertificate>1</isMainCertificate><contactCertificateTypeId>1010</contactCertificateTypeId><infoString>130204198504150013</infoString></certificateInfo><openAcctTeller>00938</openAcctTeller><whetherSmallMicroEnterprises>0</whetherSmallMicroEnterprises><custType>1</custType><customerName>冠希哥</customerName><areaCode>370500</areaCode><workUnitInfo><workMonthLyincome>0</workMonthLyincome><serialNumber>745016</serialNumber><workUnitName>同vjjjjjhjkj</workUnitName><title>1</title><workUnitAddr>你们，；i把接口</workUnitAddr><duty>1</duty><workUnitZipCode>236788</workUnitZipCode><industry>2</industry><workUnitBelongIndustry>G</workUnitBelongIndustry></workUnitInfo><medium>9998</medium><nationality>CHN</nationality><personalMonthRevenue>6999.00</personalMonthRevenue><customerGrade>00</customerGrade><lastModifiedDate>2016-10-09 14:52:15.198</lastModifiedDate><contactAddressInfo><contactAddressInfoID>1700054</contactAddressInfoID><contactAddressType>02</contactAddressType><detailAddress>顶顶顶顶顶顶根本不</detailAddress></contactAddressInfo><contactAddressInfo><contactAddressInfoID>1700055</contactAddressInfoID><contactAddressType>15</contactAddressType><detailAddress>顶顶顶顶顶顶根本不</detailAddress></contactAddressInfo></businessQueryPersonDetail></SvcBody><RspSvcHeader><returnCode>000000000000</returnCode><returnMsg>success</returnMsg></RspSvcHeader><sys_channel_name>tcpxml2http_ep</sys_channel_name></ROOT>");
				Charset gbkCharset = Charset.forName("GBK");
				int nMsgLength = rspMsg.getBytes(gbkCharset).length;
				ByteBuf sendBuf = Unpooled.buffer();
				String sLen = String.format("%08d", nMsgLength);
				sendBuf.writeBytes(sLen.getBytes());
				sendBuf.writeBytes(rspMsg.getBytes(gbkCharset));
				
				ctx.writeAndFlush(sendBuf);
				log.info("ans the client:" + rspMsg);
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				ReferenceCountUtil.release(msg);
			}
		}

		// 异常情况处理
		@Override
		public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
			// Close the connection when an exception is raised.
			log.info("服务端exceptionCaught被调用");
			cause.printStackTrace();
			ctx.close();
		}
	}

	// 客户端请求处理对象
	private class RChildHandler extends ChannelInitializer<SocketChannel> {
		@Override
		protected void initChannel(SocketChannel socketChannel) throws Exception {
			ChannelPipeline pipeline = socketChannel.pipeline();
			pipeline.addLast("idlestatus", new IdleStateHandler(getTimeout(), getTimeout(), getTimeout()));
			
			ByteBuf delimiter = Unpooled.copiedBuffer("$".getBytes());
			pipeline.addLast(new DelimiterBasedFrameDecoder(2048, delimiter));	// 增加带有分隔符的解码器
			pipeline.addLast(new StringDecoder());
			pipeline.addLast(new RDiscardServerHandler());
		}
	}

	/**
	 * @author raohj
	 * @date 2019年3月25日
	 * @comment 启动监听
	 * @param 
	 * @return 
	 */
	public void listen() {
		EventLoopGroup bossGroup = new NioEventLoopGroup(); 	// 主线程
		EventLoopGroup workerGroup = new NioEventLoopGroup(getWorkThreads()); 	// 工作线程
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
			.childHandler(new RChildHandler())
			.option(ChannelOption.SO_BACKLOG, 128);
			// 绑定端口，并且进行监听
			ChannelFuture f = b.bind(this.getPort()).sync();

			f.channel().closeFuture().sync();
			log.info("服务器端正常关闭");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}